﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;

namespace BoriPeter.NutritionManager.FatSecret
{
    public class MessageBase
    {
        public static T XmlDeserializeFromString<T>(string objectData)
        {
            return (T)XmlDeserializeFromString(objectData, typeof(T));
        }

        public static object XmlDeserializeFromString(string objectData, Type type)
        {
            object result;
            try
            {
                var serializer = new XmlSerializer(type);

                using (TextReader reader = new StringReader(objectData))
                {
                    result = serializer.Deserialize(reader);
                }
            }
            catch (Exception)
            {
                result = null;
                ErrorModel errorResult;
                var serializer = new XmlSerializer(typeof(ErrorModel));

                using (TextReader reader = new StringReader(objectData))
                {
                    errorResult = (ErrorModel)serializer.Deserialize(reader);
                }
                if (errorResult != null)
                {
                    Debug.WriteLine("Error Code: {0}, Error Message: {1}", errorResult.Code, errorResult.Message);
                }
            }

            return result;
        }

        #region Constants
        protected const string OAUTH_CONSUMER_KEY = "oauth_consumer_key";
        protected const string OAUTH_SIGNATURE_METHOD = "oauth_signature_method";
        protected const string OAUTH_TIMESTAMP = "oauth_timestamp";
        protected const string OAUTH_NONCE = "oauth_nonce";
        protected const string OAUTH_VERSION = "oauth_version";
        protected const string OAUTH_TOKEN = "oauth_token";
        private const string OAUTH_SIGNATURE_METHOD_VALUE = "HMAC-SHA1";

        protected const string OAUTH_CONSUMER_KEY_VALUE = "26c85e8ac61c4236aea846dccb645fa8";
        protected const string OAUTH_SHARED_SECRET_VALUE = "caf38dead1db47269be416a06e81f25d";
        protected const string OAUTH_VERSION_VALUE = "1.0";

        protected const string METHOD = "method";
        #endregion

        protected string OAuthUrlBase = "http://platform.fatsecret.com/rest/server.api";
        protected string OAuthTimestamp;
        protected string OAuthNonce;

        protected static string oauthToken;
        public static string OAuthToken
        {
            get
            {
                if (oauthToken == null)
                {
                    //oauthToken = FatSecretTokenManager.GetToken();
                }
                return oauthToken;
            }
            set
            {
                oauthToken = value;
            }
        }

        protected static string oauthTokenSecret;
        public static string OAuthTokenSecret
        {
            get
            {
                if (oauthTokenSecret == null)
                {
                    //oauthTokenSecret = FatSecretTokenManager.GetSecret();
                }
                return oauthTokenSecret;
            }
            set
            {
                oauthTokenSecret = value;
                //TokenCredentialManager.Store(oauthToken, oauthTokenSecret);
            }
        }

        private string requestUrl;
        protected string RequestUrl
        {
            get
            {
                if (!IsMessageSigned)
                    SignMessage();
                return requestUrl;
            }
            private set
            {
                requestUrl = value;
            }
        }

        private Dictionary<string, string> parameters;

        public Dictionary<string, string> Parameters
        {
            get
            {
                if (parameters == null)
                    parameters = new Dictionary<string, string>();
                return parameters;
            }
            set { parameters = value; }
        }

        private Boolean IsMessageSigned;

        #region MessageParametes
        private string messageParameters;
        private string MessageParameters
        {
            get
            {
                if (!IsMessageSigned)
                {
                    messageParameters = "";
                    bool isFirst = true;
                    var list = Parameters.Keys.ToList();
                    list.Sort();

                    foreach (var key in list)
                    {
                        if (isFirst) isFirst = false;
                        else messageParameters += "&";
                        messageParameters += Uri.EscapeDataString(key) + "=" + Uri.EscapeDataString(Parameters[key]);
                    }
                }
                return messageParameters;
            }
        }
        #endregion

        #region SendDataAsync
        protected async Task<string> SendDataAsync(String Url)
        {
            string response = "";
            try
            {
                using (HttpClient httpClient = new HttpClient())
                {
                    response = await httpClient.GetStringAsync(Url);
                }
            }
            catch (Exception)
            {
                //rootPage.NotifyUser("Error getting data from server." + Err.Message, NotifyType.StatusMessage);
            }
            return response;
        }
        #endregion

        #region AddMessageParameter method
        protected void AddMessageParameter(string name, string value)
        {
            Parameters.Add(name, value);
        }
        protected void AddMessageParameter(string name, DateTime date)
        {
            AddMessageParameter(name, DateToDaysSince1970(date));
        }
        protected void AddMessageParameter(string name, int integerValue)
        {
            AddMessageParameter(name, integerValue.ToString());
        }
        protected void AddMessageParameter(string name, float floatValue)
        {
            AddMessageParameter(name, floatValue.ToString());
        }
        #endregion

        #region Private Request Methods
        private void SignMessage()
        {
            if (!IsMessageSigned)
            {
                String SigBaseString = "GET&";
                SigBaseString += Uri.EscapeDataString(OAuthUrlBase) + "&" + Uri.EscapeDataString(MessageParameters);

                HMACSHA1 hmacsha1 = new HMACSHA1();
                hmacsha1.Key = Encoding.ASCII.GetBytes(OAUTH_SHARED_SECRET_VALUE + "&" + OAuthTokenSecret);

                byte[] dataBuffer = System.Text.Encoding.ASCII.GetBytes(SigBaseString);
                byte[] hashBytes = hmacsha1.ComputeHash(dataBuffer);
                
                String Signature = Convert.ToBase64String(hashBytes);

                RequestUrl = OAuthUrlBase + "?" + MessageParameters + "&oauth_signature=" + Uri.EscapeDataString(Signature);
                IsMessageSigned = true;
            }
            else
            {
                //throw new FatSecretException(FatSecretException.RESIGN_NOT_ALLOWED);
            }
        }

        protected string GenerateNonce()
        {
            Random Rand = new Random();
            Int32 Nonce = Rand.Next(1000000000);
            return Nonce.ToString();
        }

        protected string GenerateTimestamp()
        {
            TimeSpan SinceEpoch = DateTime.UtcNow - new DateTime(1970, 1, 1);
            var totalSeconds = Math.Round(SinceEpoch.TotalSeconds);
            return totalSeconds.ToString();
        }

        protected string DateToDaysSince1970(DateTime date)
        {
            TimeSpan SinceEpoch = date - new DateTime(1970, 1, 1);
            var totalDays = Math.Round(SinceEpoch.TotalDays) - 1;
            return totalDays.ToString();
        }
        #endregion

        #region Constructor
        public MessageBase()
        {
            IsMessageSigned = false;
            AddMessageParameter(OAUTH_CONSUMER_KEY, OAUTH_CONSUMER_KEY_VALUE);
            AddMessageParameter(OAUTH_VERSION, OAUTH_VERSION_VALUE);
            AddMessageParameter(OAUTH_NONCE, GenerateNonce());
            AddMessageParameter(OAUTH_TIMESTAMP, GenerateTimestamp());
            AddMessageParameter(OAUTH_SIGNATURE_METHOD, OAUTH_SIGNATURE_METHOD_VALUE);
        }
        #endregion

        public override string ToString()
        {
            return RequestUrl;
        }
    }
}
